/*
 * Copyright © 2017 eqxiu.com 北京中网易企秀科技有限公司  All rights reserved.
 */
package cn.hermit.util;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Path utility
 * 
 * @author Jack Gao (Chinese name : GAO JIANGUO, Email : linux.gjg@gmail.com)
 * @date 18 Jun, 2014
 */
public class PathUtils {

	/**
	 * IP Regular expression.
	 */
	private static final String IP_PATTERN = "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
			+ "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
			+ "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
			+ "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$";

	/**
	 * IP Regular to getHttpRequest URL string between multiple slashes started or ended.<br/>
	 * group[1] is the URL excluding multiple slashes started or ended
	 * 
	 * <p>
	 * If the URL string likes this: ///a/b/c/d///, the capture group 1 should be a/b/c/d
	 * </p>
	 */
	private static final Pattern PATTERN_START_END_SLASHES = Pattern
			.compile("/*([^/]+.*[^/]+)/*");

	/**
	 * Regular expression for URL splitting.
	 * 
	 * <p>
	 * Suppose the URL string is <i>http://localhost:9000/a/b/c/d/e/</i>
	 * group[1] is the base URL <i>http://localhost:9000</i><br>
	 * group[2] is the part before address in base URL <i>http://</i><br>
	 * group[3] is the protocol part <i>http</i><br>
	 * group[4] is the host name part with port if it is defined <i>localhost:9000</i><br>
	 * group[5] is the address part <i>localhost</i><br>
	 * group[6] is the part behind address in host name <i>:9000</i><br>
	 * group[7] is the port part if it exists , otherwise it is null <i>9000</i><br>
	 * group[8] is the URI part <i>/a/b/c/d/e/</i><br>
	 * </p>
	 */
	private static final String REGEX_URL_SPLIT = "^(((\\w+)://)?(([^:/\\s]+)(:(\\d+))?))?(/.+)?$";

	/**
	 * Compiled pattern of regular expression for URL splitting
	 */
	private static final Pattern PATTERN_URL_SPLIT = Pattern
			.compile(REGEX_URL_SPLIT);

	/**
	 * Compiled pattern of regular expression for path entries splitting.<br/>
	 * Split all path entries by slash or multiple slashes.
	 * e.g.<pre>
	 * 	the original URL string is //a//b/c///d//e////, The capture groups should be<br/>
	 * [a,b,c,d,e]
	 * </pre>
	 */
	private static final Pattern PATTERN_PATH_SPLIT = Pattern
			.compile("/*([^/]+)/*");

	/**
	 * Compiled pattern of IP regular expression.
	 */
	private static final Pattern PATTERN_IP = Pattern.compile(IP_PATTERN);

	/**
	 * Regular expression to capture RESTful parameter defined in URL string.
	 * 
	 */
	private static final String REGEX_PATH_PARAM = "\\{([^\\{\\}]*)\\}";

	/**
	 * Compiled pattern for regular expression to capture parameters defined in URL string.
	 */
	private static final Pattern PATTERN_PATH_PARAM = Pattern
			.compile(REGEX_PATH_PARAM);

	private static String IP_ADDRESS;

	static {
		try {
			IP_ADDRESS = InetAddress.getLocalHost().getHostAddress();
		} catch (UnknownHostException e) {
			IP_ADDRESS = "127.0.0.1";
		}
	}

	/**
	 * Get base URL from URL string.
	 * 
	 * @param url
	 * 		The URL string
	 * @return
	 * 		Base URL
	 */
	public static String trimBaseURL(String url) {
		if (url == null)
			return url;
		url = url.trim();
		Matcher m = PATTERN_URL_SPLIT.matcher(url);
		if (!m.matches()) {
			return null;
		}
		return m.group(1);
	}

	/**
	 * Replace base URL in URL string with new base URL
	 * @param url
	 * 		The URL string
	 * @param baseURL
	 * 		New base URL
	 * @return
	 * 		New URL string with new base URL
	 */
	public static String replaceBaseURL(String url, String baseURL) {
		String baseU = trimBaseURL(url);
		return url.replace(baseU, baseURL);
	}

	/**
	 * Get URI from URL string
	 * 
	 * @param url
	 * 		The URL string
	 * @return
	 * 		URI string
	 */
	public static String trimURI(String url) {
		if (url == null)
			return url;
		url = url.trim();
		Matcher m = PATTERN_START_END_SLASHES.matcher(url);
		if (m.matches()) {
			url = m.group(1);
		}
		m = PATTERN_URL_SPLIT.matcher(url);
		if (!m.matches()) {
			return null;
		}
		String URI = m.group(8);
		URI = URI == null ? "/" : URI.replaceAll("/{2,}", "/");
		return URI;
	}

	/**
	 * Get host name from URL string.
	 * 
	 * @param url
	 * 		The URL string
	 * @return
	 * 		Host name in URL string
	 */
	public static String trimHostName(String url) {
		if (url == null)
			return url;
		url = url.trim();
		Matcher m = PATTERN_URL_SPLIT.matcher(url);
		if (!m.matches()) {
			return null;
		}
		return m.group(4);
	}

	/**
	 * Replace host name in URL string with new host name.
	 * @param url
	 * 		The URL string
	 * @param hostname
	 * 		New host name
	 * @return
	 * 		New URL string with new host name
	 */
	public static String replaceHostName(String url, String hostname) {
		String hn = trimHostName(url);
		return url.replace(hn, hostname);
	}

	/**
	 * Get address string from URL string.
	 * @param url
	 * 		The URL string
	 * @return
	 * 		Address string in URL string
	 */
	public static String trimAddress(String url) {
		if (url == null)
			return url;
		url = url.trim();
		Matcher m = PATTERN_URL_SPLIT.matcher(url);
		if (!m.matches()) {
			return null;
		}
		return m.group(5);
	}

	/**
	 * Replace address in URL string with new address.
	 * @param url
	 * 		The URL string
	 * @param address
	 * 		New address
	 * @return
	 * 		New URL string with new address
	 */
	public static String replaceAddress(String url, String address) {
		String addr = trimAddress(url);
		return url.replace(addr, address);
	}

	/**
	 * Get port in URL string.
	 * 
	 * @param url
	 * 		The URL string
	 * @return
	 * 		Port in URL string
	 */
	public static String trimPort(String url) {
		if (url == null)
			return url;
		url = url.trim();
		Matcher m = PATTERN_URL_SPLIT.matcher(url);
		if (!m.matches()) {
			return null;
		}
		return m.group(7);
	}

	/**
	 * Replace port in URL string with new port
	 * @param url
	 * 		The URL string
	 * @param port
	 * 		New port
	 * @return
	 * 		New URL string with new port
	 */
	public static String replacePort(String url, String port) {
		String p = trimPort(url);
		return url.replace(p, port);
	}

	/**
	 * Remove redundant slashes in URL or URI.
	 * 
	 * @param path
	 * 		URL or URI
	 * @return
	 * 		Trimmed URL or URI
	 */
	public static String trimSlash(String path) {
		return trimSlash(path, false);
	}

	/**
	 * Remove redundant slashes in URL or URI
	 * 
	 * 
	 * @param path
	 *            URL or URI
	 * @param keepStart
	 *            if start with slash
	 * 
	 * @return
	 * 		Trimmed URL or URI
	 */
	public static String trimSlash(String path, boolean keepStart) {
		if (path == null)
			return null;
		path = path.trim();
		Matcher m = PATTERN_START_END_SLASHES.matcher(path);
		if (m.matches()) {
			path = m.group(1);
		}
		if (keepStart) {
			path = "/" + path;
		}
		m = PATTERN_URL_SPLIT.matcher(path);
		if (m.matches()) {
			String baseURL = m.group(1);
			path = baseURL
					+ path.substring(baseURL.length()).replaceAll("/{2,}", "/");
		} else {
			path = path.replaceAll("/{2,}", "/");
		}
		return path;
	}

	/**
	 * Get path entries in URI.
	 * 
	 * @param URI
	 * 		The URI
	 * @return
	 * 		path entries in URI
	 */
	public static List<String> pathEntries(String URI) {
		if (URI == null)
			return null;
		Matcher m = PATTERN_PATH_SPLIT.matcher(URI);
		List<String> list = new ArrayList<String>();
		while (m.find()) {
			list.add(m.group(1));
		}
		if (list.size() == 0)
			return null;
		return list;
	}

	/**
	 * Get first path entry in URI
	 * @param sPath
	 * 		The URI
	 * @return
	 * 		First path entry in URI
	 */
	public static String firstPathEntry(String sPath) {
		if (sPath == null)
			return null;
		Matcher m = PATTERN_PATH_SPLIT.matcher(sPath);
		if (m.find()) {
			return m.group(1);
		}
		return sPath;
	}

	/**
	 * Check if the path entry is RESTful parameter.
	 *  
	 * @param pathEntry
	 * 		The path entry
	 * @return
	 */
	public static boolean isRESTPathParam(String pathEntry) {
		if (pathEntry == null) {
			return false;
		}
		return pathEntry.matches(REGEX_PATH_PARAM);
	}

	/**
	 * Get RESTful parameter name from path entry.
	 * @param pathEntry
	 * 		The path entry
	 * @return
	 * 		RESTful parameter name
	 */
	public static String trimRESTPathParamName(String pathEntry) {
		if (pathEntry == null) {
			return null;
		}
		Matcher m = PATTERN_PATH_PARAM.matcher(pathEntry);
		if (m.matches()) {
			return m.group(1);
		} else {
			return null;
		}
	}

	/**
	 * Check if the path string start with some symbols.
	 * 
	 * @param path
	 * 		The path string
	 * @param start
	 * 		Some symbols to check
	 * @param isRESTPath
	 * 		If true, will consider with RESTful parameter style
	 * @return
	 */
	public static boolean startWith(String path, String start,
			boolean isRESTPath) {
		path = trimSlash(path);
		start = trimSlash(start);
		if (path.startsWith(start)) {
			return true;
		}
		if (isRESTPath) {
			List<String> pathEntries = pathEntries(path);
			List<String> startEntries = pathEntries(start);
			if (startEntries.size() > pathEntries.size()) {
				return false;
			}
			for (int i = 0; i < pathEntries.size(); i++) {
				String p = pathEntries.get(i);
				String s = startEntries.get(i);
				if (p.matches(REGEX_PATH_PARAM) || s.matches(REGEX_PATH_PARAM)) {
					continue;
				}
				if (!p.equals(s)) {
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * Cut off the start part from path string.
	 * 
	 * @param path
	 * 		The path string
	 * @param start
	 * 		The start part
	 * @return
	 * 		The left part of the path string
	 */
	public static String cutPath(String path, String start) {
		path = trimSlash(path);
		start = trimSlash(start);
		String[] pathArr = path.split("/");
		String[] startArr = start.split("/");
		if (startArr.length > pathArr.length) {
			return null;
		}
		for (int i = 0; i < startArr.length; i++) {
			String s = startArr[i];
			String p = pathArr[i];
			if (!p.equals(s) && !s.matches(REGEX_PATH_PARAM)) {
				return null;
			}
		}
		if (startArr.length == pathArr.length) {
			return "";
		}
		StringBuilder cutted = new StringBuilder();
		for (int i = startArr.length; i < pathArr.length; i++) {
			cutted.append(pathArr[i]).append("/");
		}
		return cutted.substring(0, cutted.length() - 1);
	}

	/**
	 * Validation for base URL
	 * @param baseURL
	 * 		The base URL
	 * @return
	 */
	public static boolean validateBaseURL(String baseURL) {
		if (baseURL == null)
			return false;
		return baseURL.matches(REGEX_URL_SPLIT);
	}

	/**
	 * Validation for IP address
	 * 
	 * @param ip
	 * 		The IP address
	 * @return
	 */
	public static boolean validateIP(final String ip) {
		return PATTERN_IP.matcher(ip).matches();
	}

	/**
	 * Get local IP address.
	 * 
	 * @return
	 * 		Local IP address
	 */
	public static String getIPAddress() {
		return IP_ADDRESS;
	}
}